package liquibase.dbdoc;

import liquibase.change.Change;
import liquibase.changelog.ChangeSet;
import liquibase.database.Database;
import liquibase.exception.DatabaseException;
import liquibase.exception.DatabaseHistoryException;
import liquibase.resource.OpenOptions;
import liquibase.resource.Resource;
import liquibase.util.LiquibaseUtil;
import liquibase.util.StringUtil;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.text.DateFormat;
import java.util.Date;
import java.util.List;

public abstract class HTMLWriter {
    protected Resource outputDir;
    protected Resource baseOutputDir;
    protected Database database;

    public HTMLWriter(Resource outputDir, Database database) {
        this.outputDir = outputDir;
        this.baseOutputDir = outputDir;
        this.database = database;
    }

    protected abstract void writeCustomHTML(Writer fileWriter, Object object, List<Change> changes, Database database) throws IOException;

    private Writer createFileWriter(Object object) throws IOException {
        return new OutputStreamWriter(outputDir.resolve(DBDocUtil.toFileName(object.toString().toLowerCase()) + ".html").openOutputStream(new OpenOptions()));
    }

    public void writeHTML(Object object, List<Change> ranChanges, List<Change> changesToRun, String changeLog) throws IOException, DatabaseHistoryException, DatabaseException {


        try (Writer fileWriter = createFileWriter(object)) {
            fileWriter.append("<html>");
            writeHeader(object, fileWriter);
            fileWriter.append("<body BGCOLOR=\"white\" onload=\"windowTitle();\">");

            fileWriter.append("<H2>").append(createTitle(object)).append("</H2>\n");

            writeBody(fileWriter, object, ranChanges, changesToRun);

            writeFooter(fileWriter, changeLog);

            fileWriter.append("</body>");
            fileWriter.append("</html>");
        }

    }

    private void writeFooter(Writer fileWriter, String changeLog) throws IOException {
        fileWriter.append("<hr>Generated: ");
        fileWriter.append(DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT).format(new Date()));
        fileWriter.append("<BR>Against: ");
        fileWriter.append(database.toString());
        fileWriter.append("<BR>Change Log: ");
        fileWriter.append(changeLog);
        fileWriter.append("<BR><BR>Generated By: ");
        fileWriter.append("<a href='https://www.liquibase.com' target='_TOP'>Liquibase ").append(LiquibaseUtil
        .getBuildVersionInfo()).append("</a>");
    }

    protected void writeBody(Writer fileWriter, Object object, List<Change> ranChanges, List<Change> changesToRun) throws IOException, DatabaseHistoryException, DatabaseException {
        writeCustomHTML(fileWriter, object, ranChanges, database);
        writeChanges("Pending Changes", fileWriter, changesToRun);
        writeChanges("Past Changes", fileWriter, ranChanges);
    }

    protected void writeTable(String title, List<List<String>> cells, Writer fileWriter) throws IOException {
        fileWriter.append("<P>");
        int colspan = 0;
        if (cells.isEmpty()) {
            colspan = 0;
        } else {
            colspan = cells.get(0).size();
        }
        fileWriter.append("<TABLE BORDER=\"1\" WIDTH=\"100%\" CELLPADDING=\"3\" CELLSPACING=\"0\" SUMMARY=\"\">\n")
                .append("<TR BGCOLOR=\"#CCCCFF\" CLASS=\"TableHeadingColor\">\n").append("<TD COLSPAN=").append(String.valueOf(colspan)).append("><FONT SIZE=\"+2\">\n").append("<B>").append(title).append("</B></FONT></TD>\n")
                .append("</TR>\n");

        for (List<String> row : cells) {
            fileWriter.append("<TR BGCOLOR=\"white\" CLASS=\"TableRowColor\">\n");
            for (String cell : row) {
                writeTD(fileWriter, cell);
            }
            fileWriter.append("</TR>\n");
        }
        fileWriter.append("</TABLE>\n");
    }

    private void writeTD(Writer fileWriter, String filePath) throws IOException {
        fileWriter.append("<TD VALIGN=\"top\">\n");
        fileWriter.append(filePath);
        fileWriter.append("</TD>\n");
    }

    private void writeHeader(Object object, Writer fileWriter) throws IOException {
        String title = createTitle(object);
        fileWriter.append("<head>")
                .append("<meta charset=\"utf-8\"/><title>").append(title).append("</title>")
                .append("<LINK REL =\"stylesheet\" TYPE=\"text/css\" HREF=\"../../stylesheet.css\" TITLE=\"Style\">")
                .append("<SCRIPT type=\"text/javascript\">")
                .append("function windowTitle()")
                .append("{").append("    parent.document.title=\"").append(title.replaceAll("\"", "'")).append("\";")
                .append("}")
                .append("</SCRIPT>")
                .append("</head>");
    }

    protected abstract String createTitle(Object object);

    protected void writeChanges(String title, Writer fileWriter, List<Change> changes) throws IOException, DatabaseHistoryException, DatabaseException {
        fileWriter.append("<p><TABLE BORDER=\"1\" WIDTH=\"100%\" CELLPADDING=\"3\" CELLSPACING=\"0\" SUMMARY=\"\">\n");
        fileWriter.append("<TR BGCOLOR=\"#CCCCFF\" CLASS=\"TableHeadingColor\">\n");
        fileWriter.append("<TD COLSPAN='4'><FONT SIZE=\"+2\">\n");
        fileWriter.append("<B>");
        fileWriter.append(title);
        fileWriter.append("</B></FONT></TD>\n");
        fileWriter.append("</TR>\n");

        ChangeSet lastChangeSet = null;
        if ((changes == null) || changes.isEmpty()) {
            fileWriter.append("<tr><td>None Found</td></tr>");
        } else {
            for (Change change : changes) {
                if (!change.getChangeSet().equals(lastChangeSet)) {
                    lastChangeSet = change.getChangeSet();
                    fileWriter.append("<TR BGCOLOR=\"#EEEEFF\" CLASS=\"TableSubHeadingColor\">\n");
                    writeTD(fileWriter, "<a href='../changelogs/"+DBDocUtil.toFileName(change.getChangeSet().getFilePath().toLowerCase())+".html'>"+change.getChangeSet().getFilePath()+"</a>");
                    writeTD(fileWriter, change.getChangeSet().getId());
                    writeTD(fileWriter, "<a href='../authors/"+DBDocUtil.toFileName(change.getChangeSet().getAuthor().toLowerCase())+".html'>"+ StringUtil.escapeHtml(change.getChangeSet().getAuthor().toLowerCase())+"</a>");

                    ChangeSet.RunStatus runStatus = database.getRunStatus(change.getChangeSet());
                    if (runStatus.equals(ChangeSet.RunStatus.NOT_RAN)) {
                        String anchor = change.getChangeSet().toString(false).replaceAll("\\W","_");
                        writeTD(fileWriter, "NOT YET RAN [<a href='../pending/sql.html#"+ anchor +"'>SQL</a>]");
                    } else if (runStatus.equals(ChangeSet.RunStatus.INVALID_MD5SUM)) {
                        writeTD(fileWriter, "INVALID MD5SUM");
                    } else if (runStatus.equals(ChangeSet.RunStatus.ALREADY_RAN)) {
                        writeTD(fileWriter, "Executed "+ DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT).format(database.getRanDate(change.getChangeSet())));
                    } else if (runStatus.equals(ChangeSet.RunStatus.RUN_AGAIN)) {
                        writeTD(fileWriter, "Executed, WILL RUN AGAIN");
                    } else {
                        throw new RuntimeException("Unknown run status: "+runStatus);
                    }

                    fileWriter.append("</TR>");

                    if (StringUtil.trimToNull(change.getChangeSet().getComments()) != null) {
                        fileWriter.append("<TR><TD BGCOLOR='#EEEEFF' CLASS='TableSubHeadingColor' colspan='4'>").append(change.getChangeSet().getComments()).append("</TD></TR>");
                    }

                }

                fileWriter.append("<TR BGCOLOR=\"white\" CLASS=\"TableRowColor\">\n");
                fileWriter.append("<td colspan='4'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;").append(change.getConfirmationMessage()).append("</td></TR>");
            }
        }

        fileWriter.append("</TABLE>");
        fileWriter.append("&nbsp;</P>");

    }
}
